/**   
* @Title: PropertyValuesHolder.java 
* @Package com.actionbarsherlock.internal.nineoldandroids.animation 
* @Description: 
*			声明:
*			1)掌控校园是本人的大学的创业作品,也是本人的毕业设计
*			2)本程序尚未进行开放源代码,所以禁止组织或者是个人泄露源码,否则将会追究其刑事责任
*			3)编写本软件,我需要感谢彭老师以及其他鼓励和支持我的同学以及朋友
*			4)本程序的最终所有权属于本人	
* @author  张雷 794857063@qq.com
* @date 2013-11-14 19:45:01 
* @version V1.0   
*/

package com.actionbarsherlock.internal.nineoldandroids.animation;

//import android.util.FloatProperty;
//import android.util.IntProperty;
import android.util.Log;
//import android.util.Property;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

// TODO: Auto-generated Javadoc
/**
 * This class holds information about a property and the values that that
 * property should take on during an animation. PropertyValuesHolder objects can
 * be used to create animations with ValueAnimator or ObjectAnimator that
 * operate on several different properties in parallel.
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class PropertyValuesHolder implements Cloneable {

	/**
	 * The name of the property associated with the values. This need not be a
	 * real property, unless this object is being used with ObjectAnimator. But
	 * this is the name by which aniamted values are looked up with
	 * getAnimatedValue(String) in ValueAnimator.
	 */
	String mPropertyName;

	/** The m setter. @hide */
	// protected Property mProperty;

	/**
	 * The setter function, if needed. ObjectAnimator hands off this
	 * functionality to PropertyValuesHolder, since it holds all of the
	 * per-property information. This property is automatically derived when the
	 * animation starts in setupSetterAndGetter() if using ObjectAnimator.
	 */
	Method mSetter = null;

	/**
	 * The getter function, if needed. ObjectAnimator hands off this
	 * functionality to PropertyValuesHolder, since it holds all of the
	 * per-property information. This property is automatically derived when the
	 * animation starts in setupSetterAndGetter() if using ObjectAnimator. The
	 * getter is only derived and used if one of the values is null.
	 */
	private Method mGetter = null;

	/**
	 * The type of values supplied. This information is used both in deriving
	 * the setter/getter functions and in deriving the type of TypeEvaluator.
	 */
	Class mValueType;

	/**
	 * The set of keyframes (time/value pairs) that define this animation.
	 */
	KeyframeSet mKeyframeSet = null;

	// type evaluators for the primitive types handled by this implementation
	/** The Constant sIntEvaluator. */
	private static final TypeEvaluator sIntEvaluator = new IntEvaluator();
	
	/** The Constant sFloatEvaluator. */
	private static final TypeEvaluator sFloatEvaluator = new FloatEvaluator();

	// We try several different types when searching for appropriate
	// setter/getter functions.
	// The caller may have supplied values in a type that does not match the
	// setter/getter
	// functions (such as the integers 0 and 1 to represent floating point
	// values for alpha).
	// Also, the use of generics in constructors means that we end up with the
	// Object versions
	// of primitive types (Float vs. float). But most likely, the setter/getter
	// functions
	// will take primitive types instead.
	// So we supply an ordered array of other types to try before giving up.
	/** The float variants. */
	private static Class[] FLOAT_VARIANTS = { float.class, Float.class,
			double.class, int.class, Double.class, Integer.class };
	
	/** The integer variants. */
	private static Class[] INTEGER_VARIANTS = { int.class, Integer.class,
			float.class, double.class, Float.class, Double.class };
	
	/** The double variants. */
	private static Class[] DOUBLE_VARIANTS = { double.class, Double.class,
			float.class, int.class, Float.class, Integer.class };

	// These maps hold all property entries for a particular class. This map
	// is used to speed up property/setter/getter lookups for a given
	// class/property
	// combination. No need to use reflection on the combination more than once.
	/** The Constant sSetterPropertyMap. */
	private static final HashMap<Class, HashMap<String, Method>> sSetterPropertyMap = new HashMap<Class, HashMap<String, Method>>();
	
	/** The Constant sGetterPropertyMap. */
	private static final HashMap<Class, HashMap<String, Method>> sGetterPropertyMap = new HashMap<Class, HashMap<String, Method>>();

	// This lock is used to ensure that only one thread is accessing the
	// property maps
	// at a time.
	/** The m property map lock. */
	final ReentrantReadWriteLock mPropertyMapLock = new ReentrantReadWriteLock();

	// Used to pass single value to varargs parameter in setter invocation
	/** The m tmp value array. */
	final Object[] mTmpValueArray = new Object[1];

	/**
	 * The type evaluator used to calculate the animated values. This evaluator
	 * is determined automatically based on the type of the start/end objects
	 * passed into the constructor, but the system only knows about the
	 * primitive types int and float. Any other type will need to set the
	 * evaluator to a custom evaluator for that type.
	 */
	private TypeEvaluator mEvaluator;

	/**
	 * The value most recently calculated by calculateValue(). This is set
	 * during that function and might be retrieved later either by
	 * ValueAnimator.animatedValue() or by the property-setting logic in
	 * ObjectAnimator.animatedValue().
	 */
	private Object mAnimatedValue;

	/**
	 * Internal utility constructor, used by the factory methods to set the
	 * property name.
	 * 
	 * @param propertyName
	 *            The name of the property for this holder.
	 */
	private PropertyValuesHolder(String propertyName) {
		mPropertyName = propertyName;
	}

	/**
	 * Internal utility constructor, used by the factory methods to set the
	 * property.
	 * 
	 * @param propertyName
	 *            the property name
	 * @param values
	 *            the values
	 * @return the property values holder
	 */
	// private PropertyValuesHolder(Property property) {
	// mProperty = property;
	// if (property != null) {
	// mPropertyName = property.getName();
	// }
	// }

	/**
	 * Constructs and returns a PropertyValuesHolder with a given property name
	 * and set of int values.
	 * 
	 * @param propertyName
	 *            The name of the property being animated.
	 * @param values
	 *            The values that the named property will animate between.
	 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
	 */
	public static PropertyValuesHolder ofInt(String propertyName, int... values) {
		return new IntPropertyValuesHolder(propertyName, values);
	}

	/**
	 * Constructs and returns a PropertyValuesHolder with a given property and
	 * set of int values.
	 * 
	 * @param propertyName
	 *            the property name
	 * @param values
	 *            The values that the property will animate between.
	 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
	 */
	// public static PropertyValuesHolder ofInt(Property<?, Integer> property,
	// int... values) {
	// return new IntPropertyValuesHolder(property, values);
	// }

	/**
	 * Constructs and returns a PropertyValuesHolder with a given property name
	 * and set of float values.
	 * 
	 * @param propertyName
	 *            The name of the property being animated.
	 * @param values
	 *            The values that the named property will animate between.
	 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
	 */
	public static PropertyValuesHolder ofFloat(String propertyName,
			float... values) {
		return new FloatPropertyValuesHolder(propertyName, values);
	}

	/**
	 * Constructs and returns a PropertyValuesHolder with a given property and
	 * set of float values.
	 * 
	 * @param propertyName
	 *            the property name
	 * @param evaluator
	 *            the evaluator
	 * @param values
	 *            The values that the property will animate between.
	 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
	 */
	// public static PropertyValuesHolder ofFloat(Property<?, Float> property,
	// float... values) {
	// return new FloatPropertyValuesHolder(property, values);
	// }

	/**
	 * Constructs and returns a PropertyValuesHolder with a given property name
	 * and set of Object values. This variant also takes a TypeEvaluator because
	 * the system cannot automatically interpolate between objects of unknown
	 * type.
	 * 
	 * @param propertyName
	 *            The name of the property being animated.
	 * @param evaluator
	 *            A TypeEvaluator that will be called on each animation frame to
	 *            provide the necessary interpolation between the Object values
	 *            to derive the animated value.
	 * @param values
	 *            The values that the named property will animate between.
	 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
	 */
	public static PropertyValuesHolder ofObject(String propertyName,
			TypeEvaluator evaluator, Object... values) {
		PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
		pvh.setObjectValues(values);
		pvh.setEvaluator(evaluator);
		return pvh;
	}

	/**
	 * Constructs and returns a PropertyValuesHolder with a given property and
	 * set of Object values. This variant also takes a TypeEvaluator because the
	 * system cannot automatically interpolate between objects of unknown type.
	 * 
	 * @param propertyName
	 *            the property name
	 * @param values
	 *            The values that the property will animate between.
	 * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
	 */
	// public static <V> PropertyValuesHolder ofObject(Property property,
	// TypeEvaluator<V> evaluator, V... values) {
	// PropertyValuesHolder pvh = new PropertyValuesHolder(property);
	// pvh.setObjectValues(values);
	// pvh.setEvaluator(evaluator);
	// return pvh;
	// }

	/**
	 * Constructs and returns a PropertyValuesHolder object with the specified
	 * property name and set of values. These values can be of any type, but the
	 * type should be consistent so that an appropriate
	 * {@link android.animation.TypeEvaluator} can be found that matches the
	 * common type.
	 * <p>
	 * If there is only one value, it is assumed to be the end value of an
	 * animation, and an initial value will be derived, if possible, by calling
	 * a getter function on the object. Also, if any value is null, the value
	 * will be filled in when the animation starts in the same way. This
	 * mechanism of automatically getting null values only works if the
	 * PropertyValuesHolder object is used in conjunction {@link ObjectAnimator}
	 * , and with a getter function derived automatically from
	 * <code>propertyName</code>, since otherwise PropertyValuesHolder has no
	 * way of determining what the value should be.
	 * 
	 * @param propertyName
	 *            The name of the property associated with this set of values.
	 *            This can be the actual property name to be used when using a
	 *            ObjectAnimator object, or just a name used to get animated
	 *            values, such as if this object is used with an ValueAnimator
	 *            object.
	 * @param values
	 *            The set of values to animate between.
	 */
	public static PropertyValuesHolder ofKeyframe(String propertyName,
			Keyframe... values) {
		KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
		if (keyframeSet instanceof IntKeyframeSet) {
			return new IntPropertyValuesHolder(propertyName,
					(IntKeyframeSet) keyframeSet);
		} else if (keyframeSet instanceof FloatKeyframeSet) {
			return new FloatPropertyValuesHolder(propertyName,
					(FloatKeyframeSet) keyframeSet);
		} else {
			PropertyValuesHolder pvh = new PropertyValuesHolder(propertyName);
			pvh.mKeyframeSet = keyframeSet;
			pvh.mValueType = values[0].getType();
			return pvh;
		}
	}

	/**
	 * Constructs and returns a PropertyValuesHolder object with the specified
	 * property and set of values. These values can be of any type, but the type
	 * should be consistent so that an appropriate
	 * 
	 * @param values
	 *            The set of values to animate between.
	 *            {@link android.animation.TypeEvaluator} can be found that
	 *            matches the common type.
	 *            <p>
	 *            If there is only one value, it is assumed to be the end value
	 *            of an animation, and an initial value will be derived, if
	 *            possible, by calling the property's
	 *            {@link android.util.Property#get(Object)} function. Also, if
	 *            any value is null, the value will be filled in when the
	 *            animation starts in the same way. This mechanism of
	 *            automatically getting null values only works if the
	 *            PropertyValuesHolder object is used in conjunction with
	 *            {@link ObjectAnimator}, since otherwise PropertyValuesHolder
	 *            has no way of determining what the value should be.
	 */
	// public static PropertyValuesHolder ofKeyframe(Property property,
	// Keyframe... values) {
	// KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
	// if (keyframeSet instanceof IntKeyframeSet) {
	// return new IntPropertyValuesHolder(property, (IntKeyframeSet)
	// keyframeSet);
	// } else if (keyframeSet instanceof FloatKeyframeSet) {
	// return new FloatPropertyValuesHolder(property, (FloatKeyframeSet)
	// keyframeSet);
	// }
	// else {
	// PropertyValuesHolder pvh = new PropertyValuesHolder(property);
	// pvh.mKeyframeSet = keyframeSet;
	// pvh.mValueType = ((Keyframe)values[0]).getType();
	// return pvh;
	// }
	// }

	/**
	 * Set the animated values for this object to this set of ints. If there is
	 * only one value, it is assumed to be the end value of an animation, and an
	 * initial value will be derived, if possible, by calling a getter function
	 * on the object. Also, if any value is null, the value will be filled in
	 * when the animation starts in the same way. This mechanism of
	 * automatically getting null values only works if the PropertyValuesHolder
	 * object is used in conjunction {@link ObjectAnimator}, and with a getter
	 * function derived automatically from <code>propertyName</code>, since
	 * otherwise PropertyValuesHolder has no way of determining what the value
	 * should be.
	 * 
	 * @param values
	 *            One or more values that the animation will animate between.
	 */
	public void setIntValues(int... values) {
		mValueType = int.class;
		mKeyframeSet = KeyframeSet.ofInt(values);
	}

	/**
	 * Set the animated values for this object to this set of floats. If there
	 * is only one value, it is assumed to be the end value of an animation, and
	 * an initial value will be derived, if possible, by calling a getter
	 * function on the object. Also, if any value is null, the value will be
	 * filled in when the animation starts in the same way. This mechanism of
	 * automatically getting null values only works if the PropertyValuesHolder
	 * object is used in conjunction {@link ObjectAnimator}, and with a getter
	 * function derived automatically from <code>propertyName</code>, since
	 * otherwise PropertyValuesHolder has no way of determining what the value
	 * should be.
	 * 
	 * @param values
	 *            One or more values that the animation will animate between.
	 */
	public void setFloatValues(float... values) {
		mValueType = float.class;
		mKeyframeSet = KeyframeSet.ofFloat(values);
	}

	/**
	 * Set the animated values for this object to this set of Keyframes.
	 * 
	 * @param values
	 *            One or more values that the animation will animate between.
	 */
	public void setKeyframes(Keyframe... values) {
		int numKeyframes = values.length;
		Keyframe keyframes[] = new Keyframe[Math.max(numKeyframes, 2)];
		mValueType = values[0].getType();
		for (int i = 0; i < numKeyframes; ++i) {
			keyframes[i] = values[i];
		}
		mKeyframeSet = new KeyframeSet(keyframes);
	}

	/**
	 * Set the animated values for this object to this set of Objects. If there
	 * is only one value, it is assumed to be the end value of an animation, and
	 * an initial value will be derived, if possible, by calling a getter
	 * function on the object. Also, if any value is null, the value will be
	 * filled in when the animation starts in the same way. This mechanism of
	 * automatically getting null values only works if the PropertyValuesHolder
	 * object is used in conjunction {@link ObjectAnimator}, and with a getter
	 * function derived automatically from <code>propertyName</code>, since
	 * otherwise PropertyValuesHolder has no way of determining what the value
	 * should be.
	 * 
	 * @param values
	 *            One or more values that the animation will animate between.
	 */
	public void setObjectValues(Object... values) {
		mValueType = values[0].getClass();
		mKeyframeSet = KeyframeSet.ofObject(values);
	}

	/**
	 * Determine the setter or getter function using the JavaBeans convention of
	 * setFoo or getFoo for a property named 'foo'. This function figures out
	 * what the name of the function should be and uses reflection to find the
	 * Method with that name on the target object.
	 * 
	 * @param targetClass
	 *            The class to search for the method
	 * @param prefix
	 *            "set" or "get", depending on whether we need a setter or
	 *            getter.
	 * @param valueType
	 *            The type of the parameter (in the case of a setter). This type
	 *            is derived from the values set on this PropertyValuesHolder.
	 *            This type is used as a first guess at the parameter type, but
	 *            we check for methods with several different types to avoid
	 *            problems with slight mis-matches between supplied values and
	 *            actual value types used on the setter.
	 * @return Method the method associated with mPropertyName.
	 */
	private Method getPropertyFunction(Class targetClass, String prefix,
			Class valueType) {
		// TODO: faster implementation...
		Method returnVal = null;
		String methodName = getMethodName(prefix, mPropertyName);
		Class args[] = null;
		if (valueType == null) {
			try {
				returnVal = targetClass.getMethod(methodName, args);
			} catch (NoSuchMethodException e) {
				Log.e("PropertyValuesHolder", targetClass.getSimpleName()
						+ " - " + "Couldn't find no-arg method for property "
						+ mPropertyName + ": " + e);
			}
		} else {
			args = new Class[1];
			Class typeVariants[];
			if (mValueType.equals(Float.class)) {
				typeVariants = FLOAT_VARIANTS;
			} else if (mValueType.equals(Integer.class)) {
				typeVariants = INTEGER_VARIANTS;
			} else if (mValueType.equals(Double.class)) {
				typeVariants = DOUBLE_VARIANTS;
			} else {
				typeVariants = new Class[1];
				typeVariants[0] = mValueType;
			}
			for (Class typeVariant : typeVariants) {
				args[0] = typeVariant;
				try {
					returnVal = targetClass.getMethod(methodName, args);
					// change the value type to suit
					mValueType = typeVariant;
					return returnVal;
				} catch (NoSuchMethodException e) {
					// Swallow the error and keep trying other variants
				}
			}
			// If we got here, then no appropriate function was found
			Log.e("PropertyValuesHolder",
					"Couldn't find " + prefix + "ter property " + mPropertyName
							+ " for " + targetClass.getSimpleName()
							+ " with value type " + mValueType);
		}

		return returnVal;
	}

	/**
	 * Returns the setter or getter requested. This utility function checks
	 * whether the requested method exists in the propertyMapMap cache. If not,
	 * it calls another utility function to request the Method from the
	 * targetClass directly.
	 * 
	 * @param targetClass
	 *            The Class on which the requested method should exist.
	 * @param propertyMapMap
	 *            The cache of setters/getters derived so far.
	 * @param prefix
	 *            "set" or "get", for the setter or getter.
	 * @param valueType
	 *            The type of parameter passed into the method (null for
	 *            getter).
	 * @return Method the method associated with mPropertyName.
	 */
	private Method setupSetterOrGetter(Class targetClass,
			HashMap<Class, HashMap<String, Method>> propertyMapMap,
			String prefix, Class valueType) {
		Method setterOrGetter = null;
		try {
			// Have to lock property map prior to reading it, to guard against
			// another thread putting something in there after we've checked it
			// but before we've added an entry to it
			mPropertyMapLock.writeLock().lock();
			HashMap<String, Method> propertyMap = propertyMapMap
					.get(targetClass);
			if (propertyMap != null) {
				setterOrGetter = propertyMap.get(mPropertyName);
			}
			if (setterOrGetter == null) {
				setterOrGetter = getPropertyFunction(targetClass, prefix,
						valueType);
				if (propertyMap == null) {
					propertyMap = new HashMap<String, Method>();
					propertyMapMap.put(targetClass, propertyMap);
				}
				propertyMap.put(mPropertyName, setterOrGetter);
			}
		} finally {
			mPropertyMapLock.writeLock().unlock();
		}
		return setterOrGetter;
	}

	/**
	 * Utility function to get the setter from targetClass.
	 * 
	 * @param targetClass
	 *            The Class on which the requested method should exist.
	 */
	void setupSetter(Class targetClass) {
		mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set",
				mValueType);
	}

	/**
	 * Utility function to get the getter from targetClass.
	 * 
	 * @param targetClass
	 *            the new up getter
	 */
	private void setupGetter(Class targetClass) {
		mGetter = setupSetterOrGetter(targetClass, sGetterPropertyMap, "get",
				null);
	}

	/**
	 * Internal function (called from ObjectAnimator) to set up the setter and
	 * getter prior to running the animation. If the setter has not been
	 * manually set for this object, it will be derived automatically given the
	 * property name, target object, and types of values supplied. If no getter
	 * has been set, it will be supplied iff any of the supplied values was
	 * null. If there is a null value, then the getter (supplied or derived)
	 * will be called to set those null values to the current value of the
	 * property on the target object.
	 * 
	 * @param target
	 *            The object on which the setter (and possibly getter) exist.
	 */
	void setupSetterAndGetter(Object target) {
		// if (mProperty != null) {
		// // check to make sure that mProperty is on the class of target
		// try {
		// Object testValue = mProperty.get(target);
		// for (Keyframe kf : mKeyframeSet.mKeyframes) {
		// if (!kf.hasValue()) {
		// kf.setValue(mProperty.get(target));
		// }
		// }
		// return;
		// } catch (ClassCastException e) {
		// Log.e("PropertyValuesHolder","No such property (" +
		// mProperty.getName() +
		// ") on target object " + target + ". Trying reflection instead");
		// mProperty = null;
		// }
		// }
		Class targetClass = target.getClass();
		if (mSetter == null) {
			setupSetter(targetClass);
		}
		for (Keyframe kf : mKeyframeSet.mKeyframes) {
			if (!kf.hasValue()) {
				if (mGetter == null) {
					setupGetter(targetClass);
				}
				try {
					kf.setValue(mGetter.invoke(target));
				} catch (InvocationTargetException e) {
					Log.e("PropertyValuesHolder", e.toString());
				} catch (IllegalAccessException e) {
					Log.e("PropertyValuesHolder", e.toString());
				}
			}
		}
	}

	/**
	 * Utility function to set the value stored in a particular Keyframe. The
	 * value used is whatever the value is for the property name specified in
	 * the keyframe on the target object.
	 * 
	 * @param target
	 *            The target object from which the current value should be
	 *            extracted.
	 * @param kf
	 *            The keyframe which holds the property name and value.
	 */
	private void setupValue(Object target, Keyframe kf) {
		// if (mProperty != null) {
		// kf.setValue(mProperty.get(target));
		// }
		try {
			if (mGetter == null) {
				Class targetClass = target.getClass();
				setupGetter(targetClass);
			}
			kf.setValue(mGetter.invoke(target));
		} catch (InvocationTargetException e) {
			Log.e("PropertyValuesHolder", e.toString());
		} catch (IllegalAccessException e) {
			Log.e("PropertyValuesHolder", e.toString());
		}
	}

	/**
	 * This function is called by ObjectAnimator when setting the start values
	 * for an animation. The start values are set according to the current
	 * values in the target object. The property whose value is extracted is
	 * whatever is specified by the propertyName of this PropertyValuesHolder
	 * object.
	 * 
	 * @param target
	 *            The object which holds the start values that should be set.
	 */
	void setupStartValue(Object target) {
		setupValue(target, mKeyframeSet.mKeyframes.get(0));
	}

	/**
	 * This function is called by ObjectAnimator when setting the end values for
	 * an animation. The end values are set according to the current values in
	 * the target object. The property whose value is extracted is whatever is
	 * specified by the propertyName of this PropertyValuesHolder object.
	 * 
	 * @param target
	 *            The object which holds the start values that should be set.
	 */
	void setupEndValue(Object target) {
		setupValue(target,
				mKeyframeSet.mKeyframes.get(mKeyframeSet.mKeyframes.size() - 1));
	}

	/** 
	* <p>Title: clone</p> 
	* <p>Description: </p> 
	* @return 
	* @see java.lang.Object#clone() 
	*/
	@Override
	public PropertyValuesHolder clone() {
		try {
			PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
			newPVH.mPropertyName = mPropertyName;
			// newPVH.mProperty = mProperty;
			newPVH.mKeyframeSet = mKeyframeSet.clone();
			newPVH.mEvaluator = mEvaluator;
			return newPVH;
		} catch (CloneNotSupportedException e) {
			// won't reach here
			return null;
		}
	}

	/**
	 * Internal function to set the value on the target object, using the setter
	 * set up earlier on this PropertyValuesHolder object. This function is
	 * called by ObjectAnimator to handle turning the value calculated by
	 * ValueAnimator into a value set on the object according to the name of the
	 * property.
	 * 
	 * @param target
	 *            The target object on which the value is set
	 */
	void setAnimatedValue(Object target) {
		// if (mProperty != null) {
		// mProperty.set(target, getAnimatedValue());
		// }
		if (mSetter != null) {
			try {
				mTmpValueArray[0] = getAnimatedValue();
				mSetter.invoke(target, mTmpValueArray);
			} catch (InvocationTargetException e) {
				Log.e("PropertyValuesHolder", e.toString());
			} catch (IllegalAccessException e) {
				Log.e("PropertyValuesHolder", e.toString());
			}
		}
	}

	/**
	 * Internal function, called by ValueAnimator, to set up the TypeEvaluator
	 * that will be used to calculate animated values.
	 */
	void init() {
		if (mEvaluator == null) {
			// We already handle int and float automatically, but not their
			// Object
			// equivalents
			mEvaluator = (mValueType == Integer.class) ? sIntEvaluator
					: (mValueType == Float.class) ? sFloatEvaluator : null;
		}
		if (mEvaluator != null) {
			// KeyframeSet knows how to evaluate the common types - only give it
			// a custom
			// evaluator if one has been set on this class
			mKeyframeSet.setEvaluator(mEvaluator);
		}
	}

	/**
	 * The TypeEvaluator will the automatically determined based on the type of
	 * values supplied to PropertyValuesHolder. The evaluator can be manually
	 * set, however, if so desired. This may be important in cases where either
	 * the type of the values supplied do not match the way that they should be
	 * interpolated between, or if the values are of a custom type or one not
	 * currently understood by the animation system. Currently, only values of
	 * type float and int (and their Object equivalents: Float and Integer) are
	 * correctly interpolated; all other types require setting a TypeEvaluator.
	 * 
	 * @param evaluator
	 *            the new evaluator
	 */
	public void setEvaluator(TypeEvaluator evaluator) {
		mEvaluator = evaluator;
		mKeyframeSet.setEvaluator(evaluator);
	}

	/**
	 * Function used to calculate the value according to the evaluator set up
	 * for this PropertyValuesHolder object. This function is called by
	 * ValueAnimator.animateValue().
	 * 
	 * @param fraction
	 *            The elapsed, interpolated fraction of the animation.
	 */
	void calculateValue(float fraction) {
		mAnimatedValue = mKeyframeSet.getValue(fraction);
	}

	/**
	 * Sets the name of the property that will be animated. This name is used to
	 * derive a setter function that will be called to set animated values. For
	 * example, a property name of <code>foo</code> will result in a call to the
	 * function <code>setFoo()</code> on the target object. If either
	 * <code>valueFrom</code> or <code>valueTo</code> is null, then a getter
	 * function will also be derived and called.
	 * 
	 * <p>
	 * Note that the setter function derived from this property name must take
	 * the same parameter type as the <code>valueFrom</code> and
	 * <code>valueTo</code> properties, otherwise the call to the setter
	 * function will fail.
	 * </p>
	 * 
	 * @param propertyName
	 *            The name of the property being animated.
	 */
	public void setPropertyName(String propertyName) {
		mPropertyName = propertyName;
	}

	/**
	 * Sets the property that will be animated.
	 * 
	 * <p>
	 * Note that if this PropertyValuesHolder object is used with
	 * ObjectAnimator, the property must exist on the target object specified in
	 * that ObjectAnimator.
	 * </p>
	 * 
	 * @return the property name
	 */
	// public void setProperty(Property property) {
	// mProperty = property;
	// }

	/**
	 * Gets the name of the property that will be animated. This name will be
	 * used to derive a setter function that will be called to set animated
	 * values. For example, a property name of <code>foo</code> will result in a
	 * call to the function <code>setFoo()</code> on the target object. If
	 * either <code>valueFrom</code> or <code>valueTo</code> is null, then a
	 * getter function will also be derived and called.
	 */
	public String getPropertyName() {
		return mPropertyName;
	}

	/**
	 * Internal function, called by ValueAnimator and ObjectAnimator, to
	 * retrieve the value most recently calculated in calculateValue().
	 * 
	 * @return the animated value
	 */
	Object getAnimatedValue() {
		return mAnimatedValue;
	}

	/** 
	* <p>Title: toString</p> 
	* <p>Description: </p> 
	* @return 
	* @see java.lang.Object#toString() 
	*/
	@Override
	public String toString() {
		return mPropertyName + ": " + mKeyframeSet.toString();
	}

	/**
	 * Utility method to derive a setter/getter method name from a property
	 * name, where the prefix is typically "set" or "get" and the first letter
	 * of the property name is capitalized.
	 * 
	 * @param prefix
	 *            The precursor to the method name, before the property name
	 *            begins, typically "set" or "get".
	 * @param propertyName
	 *            The name of the property that represents the bulk of the
	 *            method name after the prefix. The first letter of this word
	 *            will be capitalized in the resulting method name.
	 * @return String the property name converted to a method name according to
	 *         the conventions specified above.
	 */
	static String getMethodName(String prefix, String propertyName) {
		if (propertyName == null || propertyName.length() == 0) {
			// shouldn't get here
			return prefix;
		}
		char firstLetter = Character.toUpperCase(propertyName.charAt(0));
		String theRest = propertyName.substring(1);
		return prefix + firstLetter + theRest;
	}

	/**
	 * The Class IntPropertyValuesHolder.
	 */
	static class IntPropertyValuesHolder extends PropertyValuesHolder {

		// Cache JNI functions to avoid looking them up twice
		// private static final HashMap<Class, HashMap<String, Integer>>
		// sJNISetterPropertyMap =
		// new HashMap<Class, HashMap<String, Integer>>();
		// int mJniSetter;
		// private IntProperty mIntProperty;

		/** The m int keyframe set. */
		IntKeyframeSet mIntKeyframeSet;
		
		/** The m int animated value. */
		int mIntAnimatedValue;

		/**
		 * Instantiates a new int property values holder.
		 * 
		 * @param propertyName
		 *            the property name
		 * @param keyframeSet
		 *            the keyframe set
		 */
		public IntPropertyValuesHolder(String propertyName,
				IntKeyframeSet keyframeSet) {
			super(propertyName);
			mValueType = int.class;
			mKeyframeSet = keyframeSet;
			mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
		}

		// public IntPropertyValuesHolder(Property property, IntKeyframeSet
		// keyframeSet) {
		// super(property);
		// mValueType = int.class;
		// mKeyframeSet = keyframeSet;
		// mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
		// if (property instanceof IntProperty) {
		// mIntProperty = (IntProperty) mProperty;
		// }
		// }

		/**
		 * Instantiates a new int property values holder.
		 * 
		 * @param propertyName
		 *            the property name
		 * @param values
		 *            the values
		 */
		public IntPropertyValuesHolder(String propertyName, int... values) {
			super(propertyName);
			setIntValues(values);
		}

		// public IntPropertyValuesHolder(Property property, int... values) {
		// super(property);
		// setIntValues(values);
		// if (property instanceof IntProperty) {
		// mIntProperty = (IntProperty) mProperty;
		// }
		// }

		/** 
		* <p>Title: setIntValues</p> 
		* <p>Description: </p> 
		* @param values 
		* @see com.actionbarsherlock.internal.nineoldandroids.animation.PropertyValuesHolder#setIntValues(int[]) 
		*/
		@Override
		public void setIntValues(int... values) {
			super.setIntValues(values);
			mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
		}

		/** 
		* <p>Title: calculateValue</p> 
		* <p>Description: </p> 
		* @param fraction 
		* @see com.actionbarsherlock.internal.nineoldandroids.animation.PropertyValuesHolder#calculateValue(float) 
		*/
		@Override
		void calculateValue(float fraction) {
			mIntAnimatedValue = mIntKeyframeSet.getIntValue(fraction);
		}

		/** 
		* <p>Title: getAnimatedValue</p> 
		* <p>Description: </p> 
		* @return 
		* @see com.actionbarsherlock.internal.nineoldandroids.animation.PropertyValuesHolder#getAnimatedValue() 
		*/
		@Override
		Object getAnimatedValue() {
			return mIntAnimatedValue;
		}

		/** 
		* <p>Title: clone</p> 
		* <p>Description: </p> 
		* @return 
		* @see com.actionbarsherlock.internal.nineoldandroids.animation.PropertyValuesHolder#clone() 
		*/
		@Override
		public IntPropertyValuesHolder clone() {
			IntPropertyValuesHolder newPVH = (IntPropertyValuesHolder) super
					.clone();
			newPVH.mIntKeyframeSet = (IntKeyframeSet) newPVH.mKeyframeSet;
			return newPVH;
		}

		/**
		 * Internal function to set the value on the target object, using the
		 * setter set up earlier on this PropertyValuesHolder object. This
		 * function is called by ObjectAnimator to handle turning the value
		 * calculated by ValueAnimator into a value set on the object according
		 * to the name of the property.
		 * 
		 * @param target
		 *            The target object on which the value is set
		 */
		@Override
		void setAnimatedValue(Object target) {
			// if (mIntProperty != null) {
			// mIntProperty.setValue(target, mIntAnimatedValue);
			// return;
			// }
			// if (mProperty != null) {
			// mProperty.set(target, mIntAnimatedValue);
			// return;
			// }
			// if (mJniSetter != 0) {
			// nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
			// return;
			// }
			if (mSetter != null) {
				try {
					mTmpValueArray[0] = mIntAnimatedValue;
					mSetter.invoke(target, mTmpValueArray);
				} catch (InvocationTargetException e) {
					Log.e("PropertyValuesHolder", e.toString());
				} catch (IllegalAccessException e) {
					Log.e("PropertyValuesHolder", e.toString());
				}
			}
		}

		/** 
		* <p>Title: setupSetter</p> 
		* <p>Description: </p> 
		* @param targetClass 
		* @see com.actionbarsherlock.internal.nineoldandroids.animation.PropertyValuesHolder#setupSetter(java.lang.Class) 
		*/
		@Override
		void setupSetter(Class targetClass) {
			// if (mProperty != null) {
			// return;
			// }
			// Check new static hashmap<propName, int> for setter method
			// try {
			// mPropertyMapLock.writeLock().lock();
			// HashMap<String, Integer> propertyMap =
			// sJNISetterPropertyMap.get(targetClass);
			// if (propertyMap != null) {
			// Integer mJniSetterInteger = propertyMap.get(mPropertyName);
			// if (mJniSetterInteger != null) {
			// mJniSetter = mJniSetterInteger;
			// }
			// }
			// if (mJniSetter == 0) {
			// String methodName = getMethodName("set", mPropertyName);
			// mJniSetter = nGetIntMethod(targetClass, methodName);
			// if (mJniSetter != 0) {
			// if (propertyMap == null) {
			// propertyMap = new HashMap<String, Integer>();
			// sJNISetterPropertyMap.put(targetClass, propertyMap);
			// }
			// propertyMap.put(mPropertyName, mJniSetter);
			// }
			// }
			// } catch (NoSuchMethodError e) {
			// Log.d("PropertyValuesHolder",
			// "Can't find native method using JNI, use reflection" + e);
			// } finally {
			// mPropertyMapLock.writeLock().unlock();
			// }
			// if (mJniSetter == 0) {
			// Couldn't find method through fast JNI approach - just use
			// reflection
			super.setupSetter(targetClass);
			// }
		}
	}

	/**
	 * The Class FloatPropertyValuesHolder.
	 */
	static class FloatPropertyValuesHolder extends PropertyValuesHolder {

		// Cache JNI functions to avoid looking them up twice
		// private static final HashMap<Class, HashMap<String, Integer>>
		// sJNISetterPropertyMap =
		// new HashMap<Class, HashMap<String, Integer>>();
		// int mJniSetter;
		// private FloatProperty mFloatProperty;

		/** The m float keyframe set. */
		FloatKeyframeSet mFloatKeyframeSet;
		
		/** The m float animated value. */
		float mFloatAnimatedValue;

		/**
		 * Instantiates a new float property values holder.
		 * 
		 * @param propertyName
		 *            the property name
		 * @param keyframeSet
		 *            the keyframe set
		 */
		public FloatPropertyValuesHolder(String propertyName,
				FloatKeyframeSet keyframeSet) {
			super(propertyName);
			mValueType = float.class;
			mKeyframeSet = keyframeSet;
			mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
		}

		// public FloatPropertyValuesHolder(Property property, FloatKeyframeSet
		// keyframeSet) {
		// super(property);
		// mValueType = float.class;
		// mKeyframeSet = keyframeSet;
		// mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
		// if (property instanceof FloatProperty) {
		// mFloatProperty = (FloatProperty) mProperty;
		// }
		// }

		/**
		 * Instantiates a new float property values holder.
		 * 
		 * @param propertyName
		 *            the property name
		 * @param values
		 *            the values
		 */
		public FloatPropertyValuesHolder(String propertyName, float... values) {
			super(propertyName);
			setFloatValues(values);
		}

		// public FloatPropertyValuesHolder(Property property, float... values)
		// {
		// super(property);
		// setFloatValues(values);
		// if (property instanceof FloatProperty) {
		// mFloatProperty = (FloatProperty) mProperty;
		// }
		// }

		/** 
		* <p>Title: setFloatValues</p> 
		* <p>Description: </p> 
		* @param values 
		* @see com.actionbarsherlock.internal.nineoldandroids.animation.PropertyValuesHolder#setFloatValues(float[]) 
		*/
		@Override
		public void setFloatValues(float... values) {
			super.setFloatValues(values);
			mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
		}

		/** 
		* <p>Title: calculateValue</p> 
		* <p>Description: </p> 
		* @param fraction 
		* @see com.actionbarsherlock.internal.nineoldandroids.animation.PropertyValuesHolder#calculateValue(float) 
		*/
		@Override
		void calculateValue(float fraction) {
			mFloatAnimatedValue = mFloatKeyframeSet.getFloatValue(fraction);
		}

		/** 
		* <p>Title: getAnimatedValue</p> 
		* <p>Description: </p> 
		* @return 
		* @see com.actionbarsherlock.internal.nineoldandroids.animation.PropertyValuesHolder#getAnimatedValue() 
		*/
		@Override
		Object getAnimatedValue() {
			return mFloatAnimatedValue;
		}

		/** 
		* <p>Title: clone</p> 
		* <p>Description: </p> 
		* @return 
		* @see com.actionbarsherlock.internal.nineoldandroids.animation.PropertyValuesHolder#clone() 
		*/
		@Override
		public FloatPropertyValuesHolder clone() {
			FloatPropertyValuesHolder newPVH = (FloatPropertyValuesHolder) super
					.clone();
			newPVH.mFloatKeyframeSet = (FloatKeyframeSet) newPVH.mKeyframeSet;
			return newPVH;
		}

		/**
		 * Internal function to set the value on the target object, using the
		 * setter set up earlier on this PropertyValuesHolder object. This
		 * function is called by ObjectAnimator to handle turning the value
		 * calculated by ValueAnimator into a value set on the object according
		 * to the name of the property.
		 * 
		 * @param target
		 *            The target object on which the value is set
		 */
		@Override
		void setAnimatedValue(Object target) {
			// if (mFloatProperty != null) {
			// mFloatProperty.setValue(target, mFloatAnimatedValue);
			// return;
			// }
			// if (mProperty != null) {
			// mProperty.set(target, mFloatAnimatedValue);
			// return;
			// }
			// if (mJniSetter != 0) {
			// nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
			// return;
			// }
			if (mSetter != null) {
				try {
					mTmpValueArray[0] = mFloatAnimatedValue;
					mSetter.invoke(target, mTmpValueArray);
				} catch (InvocationTargetException e) {
					Log.e("PropertyValuesHolder", e.toString());
				} catch (IllegalAccessException e) {
					Log.e("PropertyValuesHolder", e.toString());
				}
			}
		}

		/** 
		* <p>Title: setupSetter</p> 
		* <p>Description: </p> 
		* @param targetClass 
		* @see com.actionbarsherlock.internal.nineoldandroids.animation.PropertyValuesHolder#setupSetter(java.lang.Class) 
		*/
		@Override
		void setupSetter(Class targetClass) {
			// if (mProperty != null) {
			// return;
			// }
			// Check new static hashmap<propName, int> for setter method
			// try {
			// mPropertyMapLock.writeLock().lock();
			// HashMap<String, Integer> propertyMap =
			// sJNISetterPropertyMap.get(targetClass);
			// if (propertyMap != null) {
			// Integer mJniSetterInteger = propertyMap.get(mPropertyName);
			// if (mJniSetterInteger != null) {
			// mJniSetter = mJniSetterInteger;
			// }
			// }
			// if (mJniSetter == 0) {
			// String methodName = getMethodName("set", mPropertyName);
			// mJniSetter = nGetFloatMethod(targetClass, methodName);
			// if (mJniSetter != 0) {
			// if (propertyMap == null) {
			// propertyMap = new HashMap<String, Integer>();
			// sJNISetterPropertyMap.put(targetClass, propertyMap);
			// }
			// propertyMap.put(mPropertyName, mJniSetter);
			// }
			// }
			// } catch (NoSuchMethodError e) {
			// Log.d("PropertyValuesHolder",
			// "Can't find native method using JNI, use reflection" + e);
			// } finally {
			// mPropertyMapLock.writeLock().unlock();
			// }
			// if (mJniSetter == 0) {
			// Couldn't find method through fast JNI approach - just use
			// reflection
			super.setupSetter(targetClass);
			// }
		}

	}

	// native static private int nGetIntMethod(Class targetClass, String
	// methodName);
	// native static private int nGetFloatMethod(Class targetClass, String
	// methodName);
	// native static private void nCallIntMethod(Object target, int methodID,
	// int arg);
	// native static private void nCallFloatMethod(Object target, int methodID,
	// float arg);
}
